package org.dolphin.dao.impl;

import java.io.Serializable;
import java.util.List;
import org.dolphin.base.bean.UserVO;
import org.dolphin.dao.UserDao;
import org.dolphin.dao.util.SessionHelper;
import org.dolphin.entity.security.User;
import org.dolphin.util.Nvl;
import org.dolphin.service.base.UserProfileHelper;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.sql.JoinType;
import org.hibernate.transform.AliasToBeanResultTransformer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository(value = "userDao")
public class UserDaoImpl implements UserDao {
  @Autowired(required = true)
  private SessionFactory sessionFactory;

  protected DetachedCriteria buildUsersCriteria() {
    DetachedCriteria dc = DetachedCriteria.forClass(User.class);
    return dc;
  }

  @Override
  public User fetchUser(Serializable id) {
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    User user = (User) session.get(User.class, id);
    return user;
  }

  @SuppressWarnings("unchecked")
  @Override
  public User fetchUserByUsername(String username) {
    DetachedCriteria dc = DetachedCriteria.forClass(User.class);
    dc.add(Restrictions.eq("username", username));
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    List<User> list = dc.getExecutableCriteria(session).list();
    if (list != null && !list.isEmpty()) {
      return list.get(0);
    }
    return null;
  }

  @Override
  public User getActiveUser() {
    String id = UserProfileHelper.getUserIdentity();
    if (id == null) {
      return null;
    }
    User user = loadUser(id);
    return user;
  }

  @Override
  public Long getUserCount() {
    DetachedCriteria dc = buildUsersCriteria();
    dc.setProjection(Projections.rowCount());
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    Criteria criteria = dc.getExecutableCriteria(session);
    Number totalRecords = (Number) criteria.uniqueResult();
    return totalRecords.longValue();
  }

  @SuppressWarnings("unchecked")
  @Override
  public List<UserVO> getUsers(int firstResult, int maxResults, List<Order> orders) {
    DetachedCriteria dc = buildUsersCriteria();
    if (orders != null && !orders.isEmpty()) {
      for (Order order : orders) {
        dc.addOrder(order);
      }
    }
    dc.createAlias("userLocale", "ul", JoinType.INNER_JOIN);
    ProjectionList projectionList = Projections.projectionList();
    projectionList.add(Projections.id(), "id");
    projectionList.add(Projections.property("username"), "username");
    projectionList.add(Projections.property("firstname"), "firstname");
    projectionList.add(Projections.property("lastname"), "lastname");
    projectionList.add(Projections.property("address"), "address");
    projectionList.add(Projections.property("enabled"), "enabled");
    projectionList.add(Projections.property("version"), "version");
    projectionList.add(Projections.property("administrator"), "administrator");
    projectionList.add(Projections.property("ul.description"), "userLocale");
    dc.setProjection(projectionList);
    dc.setResultTransformer(new AliasToBeanResultTransformer(UserVO.class));
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    Criteria criteria = dc.getExecutableCriteria(session);
    if (firstResult > 0) {
      criteria.setFirstResult(firstResult);
    }
    if (maxResults > 0) {
      criteria.setMaxResults(maxResults);
    }
    List<UserVO> list = criteria.list();
    return list;
  }

  @Override
  public User loadUser(Serializable id) {
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    User user = (User) session.load(User.class, id);
    return user;
  }

  @Override
  public void saveUser(User user) {
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    user.setAdministrator(Nvl.nvl(user.getAdministrator(), Boolean.FALSE));
    user.setLocked(Boolean.FALSE);
    user.setEnabled(Boolean.TRUE);
    user.setInvalidAuthenticationCount(0);
    session.save(user);
  }

  @Override
  public void updateUser(User user) {
    Session session = SessionHelper.getCurrentSession(sessionFactory);
    session.update(user);
    session.flush();
  }
}
